package demo.Movebox;

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Stack;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class TXZ {
    public static void main(String[] args) {
        TxzGame txz = new TxzGame();
        txz.mainPanel.requestFocus();				//请求游戏主面板焦点
        /*new ReadMap(0);*/
    }
}

//界面类
//游戏启动主类
class TxzGame extends JFrame implements ActionListener{
    JButton btback = new JButton("上一步");
    JButton btfirst = new JButton("第一关");
    JButton btnext = new JButton("下一关");
    JButton btpre = new JButton("上一关");
    JButton btlast = new JButton("最终关");
    JButton btselect = new JButton("选择关卡");

    JMenuBar menuBar = new JMenuBar();						//菜单
    JMenu mnOption = new JMenu("选项");
    JMenu mnHelp = new JMenu("帮助");
    JMenuItem miExit = new JMenuItem("退出");
    JMenuItem miHelp = new JMenuItem("游戏规则...");

    int MaxLevel  = 5;										//设置最大关卡数

    //创建MyPanel(游戏主面板)
    MyPanel mainPanel = new MyPanel();

    public TxzGame() {										//重写构造器
        // TODO Auto-generated constructor stub
        super("推箱子");
        Toolkit toolkit = Toolkit.getDefaultToolkit();		//工具包，可以用工具包的方法把硬盘上的图片拿到内存中来
        Image image = toolkit.getImage("src/demo/File/TXZ/icon.jpg");
        //设置图标
        setIconImage(image);
        Container c = getContentPane();						//获得窗口容器，用于添加
        c.setLayout(null);									//绝对布局

        //调用创建菜单函数
        creatMenus(c);
        //调用创建按钮函数
        createButton(c);
        //根据图片像素大小设置主面板像素大小
        mainPanel.setBounds(0, 20, 600, 600);
        c.add(mainPanel);
        c.setBackground(Color.blue);
        // 添加主面板
		/*mainPanel.setBounds(0, 50, 600, 600);
		c.add(mainPanel);*/
        setSize(720,655);
        setVisible(true);
        setResizable(false);
        setLocationRelativeTo(null); 						//设置窗口居中
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    //创建按钮函数
    public void createButton(Container c) {					//要把窗口容器传进来，意为在窗口容器中进行添加操作
        // TODO Auto-generated method stub
        JPanel ButtonPanel = new JPanel();
        //设置按钮

        btback.setBackground(Color.LIGHT_GRAY);
        btfirst.setBackground(Color.LIGHT_GRAY);
        btnext.setBackground(Color.LIGHT_GRAY);
        btpre.setBackground(Color.LIGHT_GRAY);
        btlast.setBackground(Color.LIGHT_GRAY);
        btselect.setBackground(Color.LIGHT_GRAY);

        Box ButtonBox = Box.createVerticalBox();			//创建一个Box，用来盛装服务按钮，并加上间隔，使界面美观


        ButtonBox.add(btback);
        ButtonBox.add(Box.createVerticalStrut(50));			//控制间隔
        ButtonBox.add(btfirst);
        ButtonBox.add(Box.createVerticalStrut(50));
        ButtonBox.add(btnext);
        ButtonBox.add(Box.createVerticalStrut(50));
        ButtonBox.add(btpre);
        ButtonBox.add(Box.createVerticalStrut(50));
        ButtonBox.add(btlast);
        ButtonBox.add(Box.createVerticalStrut(50));
        ButtonBox.add(btselect);
        //设置按钮监听

		/*this指向的是一个对象（本类对象），但这个对象必须实现ActionListener接口
		  如果有另一个类（假设为B类）也实现了ActionListener接口，那么也可以写为A.add（new B）
		 此方法常用于写一个专门处理特定事件（A）的类（AActionListener【此类必须实现AntionListener接口】），
		那么在为事件A添加监听时可写A.addActionListener( new AActionListener);
		*/
        btback.addActionListener(this);
        btfirst.addActionListener(this);
        btnext.addActionListener(this);
        btpre.addActionListener(this);
        btlast.addActionListener(this);
        btselect.addActionListener(this);

        ButtonPanel.add(ButtonBox);
        ButtonPanel.setBounds(600, 20, 120, 660);				//如果方法中没有传进窗口容器，此处将无法添加
        c.add(ButtonPanel);
    }

    //创建菜单函数
    public void creatMenus(Container c) {
        // TODO Auto-generated method stub
        JPanel MenuPanel = new JPanel();
        mnOption.add(miExit);
        mnHelp.add(miHelp);
        menuBar.add(mnOption);
        menuBar.add(mnHelp);
        //设置菜单监听
        miExit.addActionListener(this);
        miHelp.addActionListener(this);
        menuBar.setBounds(0, 0, 720, 20);
        c.add(menuBar);										//如果不传窗口容器作为参数，此处无法添加
    }

    //监听
    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub
        if(e.getSource()==miHelp) {
            String str = "您需要将木箱全部推入黄色区域完成游戏";
            JOptionPane.showMessageDialog(this, str, "游戏规则", JOptionPane.QUESTION_MESSAGE);				//学到了！！！！！
        }else if(e.getSource()==miExit) {
            System.exit(0);
        }else if(e.getSource()==btfirst) {
            mainPanel.setLevel(0);										//创建内部类对象访问内部类成员
        }else if(e.getSource()==btnext) {
            mainPanel.setLevel(1);
        }else if(e.getSource()==btpre) {
            mainPanel.setLevel(-1);
        }else if(e.getSource()==btlast) {
            mainPanel.setLevel(MaxLevel);
        }else if(e.getSource()==btselect) {
            String chooselevel = JOptionPane.showInputDialog(this, "选择关卡1~5", "选择关卡", JOptionPane.OK_CANCEL_OPTION);
            if(chooselevel!=null) {
                try {
                    int level = Integer.parseInt(chooselevel)-1;
                    if(level<0||level>4) {
                        JOptionPane.showMessageDialog(this,"关卡不存在，请输入有效关卡数字！");
                        return;
                    }
                    mainPanel.setLevel(level);				//选关
                }catch(Exception e1) {
                    JOptionPane.showMessageDialog(this,"无效字符，请输入有效关卡数字！");
                    return;
                }
            }
        }else if(e.getSource()==btback) {					//后退一步
            mainPanel.GoBack();
            mainPanel.requestFocus();
        }

    }


    //=======================================================================================================
    //游戏主面板  内部类  键盘事件
    class MyPanel extends JPanel implements KeyListener {
        private int level;
		/*保存关卡数,int类型变量默认值为0，这里的level给readFile(int level)调用，readFile()再调用ReadMap类，并把level传给ReadMap(level)
		ReadMap(level)用这个level拼接文件路径，然后将读取的20*20的数字文件地图信息放在一个字节数组中，再利用for循环将这个数组改成一个二维数组[20][20];
		注意字节数字换成0~9的数字加入int类型的二维数组中时会转换成ASCII码，需要将每个数字-48；
		同时将与数字对应的图片放在一个image数组中。创建画笔g，用g.drawImage()方法将二维数组中的数字在image数组中以索引对应的方式画出图像，便得到游戏地图*/

        //设置关卡/此处注意越界问题
        public void setLevel(int level) {
            if(level == 0) {			//第一关
                this.level=0;
            }else if(level==5) {		//最后一关
                this.level=4;						//这里必须设置为4，否则传给raedFile时会访问不到文件
            }else if(level==1||level==-1) {		//上一关 下一关
                this.level = this.level + level;
            }else {						//选关
                this.level=level;
            }
            //加一个控制，当选择的关卡不存在时。跳转第一关或者最后一关
            if(this.level<0) {
                this.level = 0;
            }else if(this.level>4) {
                this.level = 4;						//同上
            }
            readFile(this.level); 											//设置完等级，自动画出地图

            //设置焦点
            requestFocus();

            //清空上一局的堆栈，防止在第二局中误把上一局的步骤当做是这一局的步骤发生返回一步是出现错误
            step.clear();
        }

        private int mx,my;
        private int map[][];										//原始地图数据
        private int tempMap[][];									//临时地图数据


        //游戏主要面板功能------画图
        //重写画图函数
        Toolkit tool = Toolkit.getDefaultToolkit();			//创建工具箱，用来读取硬盘中德图片；
        Image image[] = {									//创建一个数组，用来保存读取的图片；
                tool.getImage("src/demo/File/TXZ/0.png"),
                tool.getImage("src/demo/File/TXZ/1.png"),
                tool.getImage("src/demo/File/TXZ/2.png"),
                tool.getImage("src/demo/File/TXZ/3.png"),
                tool.getImage("src/demo/File/TXZ/4.png"),
                tool.getImage("src/demo/File/TXZ/5.png"),
                tool.getImage("src/demo/File/TXZ/6.png"),
                tool.getImage("src/demo/File/TXZ/7.png"),
                tool.getImage("src/demo/File/TXZ/8.png"),
                tool.getImage("src/demo/File/TXZ/9.png"),

        };

        /*实现退回一步的核心代码****************重点
         * 利用堆栈  后进先出的功能
         * 堆栈，保存推箱子的每一个步骤;********************************学到了！！！！！！！！！！！！！！！！！！
         * 在每一次动作后往集合中加入代表一个动作的记录值************************************************
         * 在每一个共方法末尾的push()
         * 向左：
         * 	没有推箱子 1
         *  推箱子        11
         * 向上：
         * 	没有推箱子 2
         *  推箱子        22
         * 向右：
         * 	没有推箱子 3
         *  推箱子        33
         * 向下：
         * 	没有推箱子 4
         *  推箱子        44
         * */
        Stack<Integer> step = new Stack<Integer>();

        public void GoBack() {									//读取对渣的内容
            // TODO Auto-generated method stub
            /*System.out.println("没有上一步了");*/
            if(step.isEmpty()) {
                JOptionPane.showMessageDialog(null, "没有上一步了");
                /*System.out.println("没有上一步了");*/
                return;
            }
            int preStep = step.pop();						//取出栈顶元素，同时弹出该元素

            switch(preStep) {
                case 1:
                case 11:
                    backLeft(preStep);
                    break;
                case 2:
                case 22:
                    backUp(preStep);
                    break;
                case 3:
                case 33:
                    backRight(preStep);
                    break;
                case 4:
                case 44:
                    backDown(preStep);
                    break;
            }
            requestFocus();
        }

        //后退一步**************************************************难点,至今闷逼！以后再也不写智力游戏
        public void backLeft(int step) {						//向左退
            // TODO Auto-generated method stub
            if(step==1) {									//没有推箱子
                tempMap[mx][my+1]=6;
                if(map[mx][my]==4||map[mx][my]==9)
                    tempMap[mx][my]=4;
                else
                    tempMap[mx][my]=2;
                my++;
            }else {											//推了箱子
                tempMap[mx][my+1]=6;
                if(map[mx][my]==4||map[mx][my]==9) {
                    tempMap[mx][my]=9;
                }
                else {
                    tempMap[mx][my]=3;
                }
                if(map[mx][my-1]==4||map[mx][my-1]==9) {
                    tempMap[mx][my-1]=4;
                }
                else {
                    tempMap[mx][my-1]=2;
                }
                my++;
            }
            repaint();
        }
        public void backUp(int step) {						//向上退
            // TODO Auto-generated method stub
            if(step==2) {									//没有推箱子
                tempMap[mx+1][my]=8;
                if(map[mx][my]==4||map[mx][my]==9)
                    tempMap[mx][my]=4;
                else
                    tempMap[mx][my]=2;
                mx++;
            }else {											//推了箱子
                tempMap[mx+1][my]=8;
                if(map[mx][my]==4||map[mx][my]==9) {
                    tempMap[mx][my]=9;
                }
                else {
                    tempMap[mx][my]=3;
                }
                if(map[mx-1][my]==4||map[mx-1][my]==9) {
                    tempMap[mx-1][my]=4;
                }
                else {
                    tempMap[mx-1][my]=2;
                }
                mx++;
            }
            repaint();
        }
        public void backRight(int step) {						//向右退
            // TODO Auto-generated method stub
            if(step==3) {									//没有推箱子
                tempMap[mx][my-1]=7;
                if(map[mx][my]==4||map[mx][my]==9)
                    tempMap[mx][my]=4;
                else
                    tempMap[mx][my]=2;
                my--;
            }else {											//推了箱子
                tempMap[mx][my-1]=7;
                if(map[mx][my]==4||map[mx][my]==9) {
                    tempMap[mx][my]=9;
                }
                else {
                    tempMap[mx][my]=3;
                }
                if(map[mx][my+1]==4||map[mx][my+1]==9) {
                    tempMap[mx][my+1]=4;
                }
                else {
                    tempMap[mx][my+1]=2;
                }
                my--;
            }
            repaint();
        }
        public void backDown(int step) {						//向下退
            // TODO Auto-generated method stub
            if(step==4) {									//没有推箱子
                tempMap[mx-1][my]=5;
                if(map[mx][my]==4||map[mx][my]==9)
                    tempMap[mx][my]=4;
                else
                    tempMap[mx][my]=2;
                mx--;
            }else {											//推了箱子
                tempMap[mx-1][my]=5;
                if(map[mx][my]==4||map[mx][my]==9) {
                    tempMap[mx][my]=9;
                }
                else {
                    tempMap[mx][my]=3;
                }
                if(map[mx+1][my]==4||map[mx+1][my]==9) {
                    tempMap[mx+1][my]=4;
                }
                else {
                    tempMap[mx+1][my]=2;
                }
                mx--;
            }
            repaint();
        }


        public void readFile(int level){					//读取特定地图，选关时调用
            //读取地图
            //创建RaedMap对象，调用get函数返回初始地图数据
            ReadMap readmap = new ReadMap(level);			//默认构造器已被传参修该
            this.map = readmap.getMap();
            this.mx = readmap.getMx();
            this.my = readmap.getMy();
            //临时数据，开始时临时地图和初始地图相同
            this.tempMap = new ReadMap(level).getMap();
            repaint();										//重画
        }

        //构造函数
        public MyPanel() {
            // TODO Auto-generated constructor stub
            readFile(level);
            //监听键盘事件
            this.addKeyListener(this);						//游戏面板加入监听面板
            setSize(600,600);
            requestFocus();
        }

        @Override
        public void paint(Graphics g) {
            // TODO Auto-generated method stub
            for(int i=0;i<20;i++) {
                for(int j=0;j<20;j++) {
                    ////此处对位置进行处理，按原数组画图时会将横纵坐标弄反，例如map[0][1]时应该画在x=1，y=0的位置，但是却画在了x=0，y=1的位置
                    g.drawImage(image[tempMap[i][j]], j*30, i*30, 30, 30, this);
                }
            }
        }

        //判断箱子是否已经完全到位,及判断数字的每一个4都变成了9
        public boolean Win() {
            boolean ok = true;
            for(int i=0;i<20;i++) {
                for(int j=0;j<20;j++) {
                    if((map[i][j]==4||map[i][j]==9)&&tempMap[i][j]!=9) {//动态地图中为目标地和完成状态下的盒子都变成完成状态下的盒子时
                        ok= false;
                        break;
                    }
                }
                if(!ok)
                    break;
            }
            return ok;
        }

        @Override
        public void keyPressed(KeyEvent e) {			//注意按键必须放在keyPressed中
            // TODO Auto-generated method stub
            //System.out.println(e.getKeyCode());
            if(e.getKeyCode()==37) {//向左
                goLeft();
            }else if(e.getKeyCode()==38) {//向上
                //System.out.println("!!!!!!!!!!");
                /*System.out.println(mx);*/
                goUp();
            }else if(e.getKeyCode()==39) {//向右
                goRight();
            }else if(e.getKeyCode()==40) {//向下
                goDown();
            }
            if(Win()) {									//如果走到哪一步完成关卡，win()返回true
                JOptionPane.showMessageDialog(null, "恭喜过关，点击确认进入下一关");
                setLevel(1); 								//进入下一关
            }
        }


        //推箱子动作核心代码+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        public void goLeft() {
            // TODO Auto-generated method stub
            if(tempMap[mx][my-1]==3||tempMap[mx][my-1]==9) {		//人的左边是箱子
                //System.out.println("11111111");
                //判断箱子的左边（mx-2）是草地(2)还是目标地（4）
                if(tempMap[mx][my-2]==2||tempMap[mx][my-2]==4) {
                    if(tempMap[mx][my-2]==2) {		//箱子的左边是草地
                        tempMap[mx][my-2]=3;		//草地位置的图片变成箱子图片
                        tempMap[mx][my-1]=6;		//箱子的位置变成向左的人物图片
                    }else {							//箱子的左边是目标地
                        tempMap[mx][my-2]=9;		//目标地的图片变成推入状态下的箱子的图片
                        tempMap[mx][my-1]=6;		//箱子的位置变成向左的人物图片
                    }
                    //与原地图进行比较
                    if(map[mx][my]==4||map[mx][my]==9) {			//原地图此处是草地或者是推好的箱子
                        tempMap[mx][my]=4;			//向左人物离开后还原为目标地
                    }else{
                        tempMap[mx][my]=2;			//向左人物离开后还原为草地
                    }
                    my--;
                    //每一次my--说明人走动了
                    step.push(11);
                }
            }
            else if(tempMap[mx][my-1]==2||tempMap[mx][my-1]==4||tempMap[mx][my-1]==5) {			//人的左边是草地或者目标地
                tempMap[mx][my-1]=6;
                //与原地图进行比较
                if(map[mx][my]==4||map[mx][my]==9) {			//原地图此处是草地或者是推好的箱子
                    tempMap[mx][my]=4;			//向左人物离开后还原为目标地
                }else{
                    tempMap[mx][my]=2;			//向左人物离开后还原为草地
                }
                my--;
                step.push(1);
            }
            repaint();
        }
        public void goUp() {
            // TODO Auto-generated method stub
            //根据人的位置mx  my判断人的上面是否有箱子或者草地
            //人的上面(mx-1)有箱子
            //System.out.println("!!!!!!!!!!");
            if(tempMap[mx-1][my]==3||tempMap[mx-1][my]==9) {		//人的上面是箱子
                //System.out.println("11111111");
                //判断箱子的上面（mx-2）是草地(2)还是目标地（4）
                if(tempMap[mx-2][my]==2||tempMap[mx-2][my]==4) {
                    if(tempMap[mx-2][my]==2) {		//箱子的上面是草地
                        tempMap[mx-2][my]=3;		//草地位置的图片变成箱子图片
                        tempMap[mx-1][my]=8;		//箱子的位置变成向上的人物图片
                    }else {							//箱子的上面是目标地
                        tempMap[mx-2][my]=9;		//目标地的图片变成推入状态下的箱子的图片
                        tempMap[mx-1][my]=8;		//箱子的位置变成向上的人物图片
                    }
                    //与原地图进行比较
                    if(map[mx][my]==4||map[mx][my]==9) {			//原地图此处是草地或者是推好的箱子
                        tempMap[mx][my]=4;			//向上人物离开后还原为目标地
                    }else{
                        tempMap[mx][my]=2;			//向上人物离开后还原为草地
                    }
                    mx--;
                    step.push(22);
                }
            }
            else if(tempMap[mx-1][my]==2||tempMap[mx-1][my]==4||tempMap[mx-1][my]==5) {			//人的上面是草地或者目标地
                tempMap[mx-1][my]=8;
                //与原地图进行比较
                if(map[mx][my]==4||map[mx][my]==9) {			//原地图此处是草地或者是推好的箱子
                    tempMap[mx][my]=4;			//向上人物离开后还原为目标地
                }else{
                    tempMap[mx][my]=2;			//向上人物离开后还原为草地
                }
                mx--;
                step.push(2);
            }
            repaint();
        }
        public void goRight() {
            if(tempMap[mx][my+1]==3||tempMap[mx][my+1]==9) {		//人的右边是箱子
                //System.out.println("11111111");
                //判断箱子的右边（mx-2）是草地(2)还是目标地（4）
                if(tempMap[mx][my+2]==2||tempMap[mx][my+2]==4) {
                    if(tempMap[mx][my+2]==2) {		//箱子的右边是草地
                        tempMap[mx][my+2]=3;		//草地位置的图片变成箱子图片
                        tempMap[mx][my+1]=7;		//箱子的位置变成向右的人物图片
                    }else {							//箱子的右边是目标地
                        tempMap[mx][my+2]=9;		//目标地的图片变成推入状态下的箱子的图片
                        tempMap[mx][my+1]=7;		//箱子的位置变成向右的人物图片
                    }
                    //与原地图进行比较
                    if(map[mx][my]==4||map[mx][my]==9) {			//原地图此处是草地或者是推好的箱子
                        tempMap[mx][my]=4;			//向右人物离开后还原为目标地
                    }else{
                        tempMap[mx][my]=2;			//向右人物离开后还原为草地
                    }
                    my++;
                    step.push(33);
                }
            }
            else if(tempMap[mx][my+1]==2||tempMap[mx][my+1]==4||tempMap[mx][my+1]==5) {			//人的右边是草地或者目标地
                tempMap[mx][my+1]=7;
                //与原地图进行比较
                if(map[mx][my]==4||map[mx][my]==9) {			//原地图此处是草地或者是推好的箱子
                    tempMap[mx][my]=4;			//向右人物离开后还原为目标地
                }else{
                    tempMap[mx][my]=2;			//向右人物离开后还原为草地
                }
                my++;
                step.push(3);
            }
            repaint();
        }
        public void goDown() {
            if(tempMap[mx+1][my]==3||tempMap[mx+1][my]==9) {		//人的下面是箱子
                //System.out.println("11111111");
                //判断箱子的下面（mx-2）是草地(2)还是目标地（4）
                if(tempMap[mx+2][my]==2||tempMap[mx+2][my]==4) {
                    if(tempMap[mx+2][my]==2) {		//箱子的下面是草地
                        tempMap[mx+2][my]=3;		//草地位置的图片变成箱子图片
                        tempMap[mx+1][my]=5;		//箱子的位置变成向下的人物图片
                    }else {							//箱子的下面是目标地
                        tempMap[mx+2][my]=9;		//目标地的图片变成推入状态下的箱子的图片
                        tempMap[mx+1][my]=5;		//箱子的位置变成向下的人物图片
                    }
                    //与原地图进行比较
                    if(map[mx][my]==4||map[mx][my]==9) {			//原地图此处是草地或者是推好的箱子
                        tempMap[mx][my]=4;			//向下人物离开后还原为目标地
                    }else{
                        tempMap[mx][my]=2;			//向下人物离开后还原为草地
                    }
                    mx++;
                    step.push(44);
                }
            }
            else if(tempMap[mx+1][my]==2||tempMap[mx+1][my]==4||tempMap[mx+1][my]==5) {			//人的下面是草地或者目标地
                tempMap[mx+1][my]=5;
                //与原地图进行比较
                if(map[mx][my]==4||map[mx][my]==9) {				//原地图此处是草地或者是推好的箱子
                    tempMap[mx][my]=4;				//向下人物离开后还原为目标地
                }else{
                    tempMap[mx][my]=2;				//向下人物离开后还原为草地
                }
                mx++;
                step.push(4);
            }
            repaint();
        }
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        @Override
        public void keyTyped(KeyEvent e) {}

        @Override
        public void keyReleased(KeyEvent e) {}
        //点击后退一步的函数

    }
    //=====================================================================================================
}